﻿//圆形Image扩展组件，支持圆形碰撞检测
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public enum ImageProEffect { None, FlowLight, Gray }

[AddComponentMenu("BToolkit/ImagePro")]
public class ImagePro : Image
{

    public float cornerRadius;
    public int cornerDetail = 5;
    public float texScale = 1;

    public bool gradient;
    public ImageProEffect effect = ImageProEffect.None;
    public Color32 beginColor = Color.yellow;
    public Color32 endColor = Color.red;
    public Texture flowLightTexture;
    public float flowLightLightScale = 2.5f;
    public float flowLightIntensity = 1;
    public float flowLightSpeed = 0.2f;
    public float flowLightAngle = 0.5f;

    private List<UIVertex> vertexs = new List<UIVertex>();
    private Material grayMaterial, flowLightMaterial;

    private void Update()
    {
        switch (effect)
        {
            case ImageProEffect.None:
                if (material != null)
                {
                    material = null;
                }
                break;
            case ImageProEffect.FlowLight:
                if (material != flowLightMaterial)
                {
                    if (!flowLightMaterial)
                    {
                        Shader shader = Resources.Load<Shader>("FlowLight");
                        flowLightMaterial = new Material(shader);
                    }
                    material = flowLightMaterial;
                }
                if (material.HasProperty("_LightTex") && material.GetTexture("_LightTex") != flowLightTexture)
                {
                    material.SetTexture("_LightTex", flowLightTexture);
                }
                if (material.GetFloat("_LightScale") != flowLightLightScale)
                {
                    material.SetFloat("_LightScale", flowLightLightScale);
                }
                if (material.GetFloat("_Intensity") != flowLightIntensity)
                {
                    material.SetFloat("_Intensity", flowLightIntensity);
                }
                if (material.GetFloat("_Speed") != flowLightSpeed)
                {
                    material.SetFloat("_Speed", flowLightSpeed);
                }
                if (material.GetFloat("_Angle") != flowLightAngle)
                {
                    material.SetFloat("_Angle", flowLightAngle);
                }
                break;
            case ImageProEffect.Gray:
                if (material != grayMaterial)
                {
                    if (!grayMaterial)
                    {
                        Shader shader = Resources.Load<Shader>("UGUIGray");
                        grayMaterial = new Material(shader);
                    }
                    material = grayMaterial;
                }
                break;
        }
    }

    protected override void OnPopulateMesh(VertexHelper vh)
    {
        if (!IsActive())
        {
            return;
        }

        if (cornerRadius <= 0)
        {
            base.OnPopulateMesh(vh);
        }
        else
        {

            vh.Clear();

            Vector2 size = new Vector2(rectTransform.rect.width, rectTransform.rect.height);
            float halfSizeX = size.x * 0.5f;
            float halfSizeY = size.y * 0.5f;
            float halfSizeMin = halfSizeX > halfSizeY ? halfSizeY : halfSizeX;
            //圆心
            Vector2 centerUV = Vector2.zero;
            AddVertex(vh, centerUV);

            //外圈
            float incRadian = Mathf.Deg2Rad * 360 / (cornerDetail * 4);
            int triangleCount = cornerDetail * 4;
            if (cornerRadius <= halfSizeMin) triangleCount += 4;

            if (type == Type.Filled)
            {
                triangleCount = Mathf.CeilToInt(triangleCount * fillAmount);
            }

            //中上三角形
            if (cornerRadius <= halfSizeMin)
            {
                Vector2 uv;
                uv.x = -halfSizeX + cornerRadius;
                uv.y = halfSizeY;
                AddVertex(vh, uv);
            }

            //右上角几个三角形
            for (int i = 0; i < cornerDetail; i++)
            {
                Vector2 uv;
                if (i == 0)
                {
                    uv.x = halfSizeX - cornerRadius;
                    uv.y = halfSizeY;
                }
                else
                {
                    float r = Mathf.PI * 0.5f - incRadian * i;
                    float centerX = halfSizeX - cornerRadius;
                    float centerY = halfSizeY - cornerRadius;
                    uv.x = centerX + Mathf.Cos(r) * cornerRadius;
                    uv.y = centerY + Mathf.Sin(r) * cornerRadius;
                }
                AddVertex(vh, uv);
            }

            //中右三角形
            if (cornerRadius <= halfSizeMin)
            {
                Vector2 uv;
                uv.x = halfSizeX;
                uv.y = halfSizeY - cornerRadius;
                AddVertex(vh, uv);
            }

            //右下角几个三角形
            for (int i = 0; i < cornerDetail; i++)
            {
                Vector2 uv;
                if (i == 0)
                {
                    uv.x = halfSizeX;
                    uv.y = -halfSizeY + cornerRadius;
                }
                else
                {
                    float r = -incRadian * i;
                    float centerX = halfSizeX - cornerRadius;
                    float centerY = -halfSizeY + cornerRadius;
                    uv.x = centerX + Mathf.Cos(r) * cornerRadius;
                    uv.y = centerY + Mathf.Sin(r) * cornerRadius;
                }
                AddVertex(vh, uv);
            }

            //中下三角形
            if (cornerRadius <= halfSizeMin)
            {
                Vector2 uv;
                uv.x = halfSizeX - cornerRadius;
                uv.y = -halfSizeY;
                AddVertex(vh, uv);
            }

            //左下角几个三角形
            for (int i = 0; i < cornerDetail; i++)
            {
                Vector2 uv;
                if (i == 0)
                {
                    uv.x = -halfSizeX + cornerRadius;
                    uv.y = -halfSizeY;
                }
                else
                {
                    float r = -Mathf.PI * 0.5f - incRadian * i;
                    float centerX = -halfSizeX + cornerRadius;
                    float centerY = -halfSizeY + cornerRadius;
                    uv.x = centerX + Mathf.Cos(r) * cornerRadius;
                    uv.y = centerY + Mathf.Sin(r) * cornerRadius;
                }
                AddVertex(vh, uv);
            }

            //中左三角形
            if (cornerRadius <= halfSizeMin)
            {
                Vector2 uv;
                uv.x = -halfSizeX;
                uv.y = -halfSizeY + cornerRadius;
                AddVertex(vh, uv);
            }

            //左上角几个三角形
            for (int i = 0; i < cornerDetail; i++)
            {
                Vector2 uv;
                if (i == 0)
                {
                    uv.x = -halfSizeX;
                    uv.y = halfSizeY - cornerRadius;
                }
                else
                {
                    float r = -Mathf.PI - incRadian * i;
                    float centerX = -halfSizeX + cornerRadius;
                    float centerY = halfSizeY - cornerRadius;
                    uv.x = centerX + Mathf.Cos(r) * cornerRadius;
                    uv.y = centerY + Mathf.Sin(r) * cornerRadius;
                }
                AddVertex(vh, uv);
            }

            AddTriangle(vh, triangleCount);
        }

        //渐变色
        if (gradient)
        {
            Gradient(vh);
        }
    }

    //渐变色
    private void Gradient(VertexHelper vh)
    {
        var count = vh.currentVertCount;
        if (count == 0)
            return;

        vertexs.Clear();
        for (var i = 0; i < count; i++)
        {
            var vertex = new UIVertex();
            vh.PopulateUIVertex(ref vertex, i);
            vertexs.Add(vertex);
        }

        var topY = vertexs[0].position.y;
        var bottomY = vertexs[0].position.y;

        for (var i = 1; i < count; i++)
        {
            var y = vertexs[i].position.y;
            if (y > topY)
            {
                topY = y;
            }
            else if (y < bottomY)
            {
                bottomY = y;
            }
        }

        var height = topY - bottomY;
        for (var i = 0; i < count; i++)
        {
            var vertex = vertexs[i];

            var color = Color32.Lerp(endColor, beginColor, (vertex.position.y - bottomY) / height);

            vertex.color = color;

            vh.SetUIVertex(vertex, i);
        }
    }

    /// <summary>
    /// 添加顶点数据
    /// </summary>
    private void AddVertex(VertexHelper vh, Vector2 uv)
    {
        UIVertex vertex = UIVertex.simpleVert;
        vertex.color = color;
        Vector2 size = new Vector2(rectTransform.rect.width, rectTransform.rect.height);
        Vector2 pivot = rectTransform.pivot;
        vertex.position.x = uv.x + size.x * (0.5f - pivot.x);
        vertex.position.y = uv.y + size.y * (0.5f - pivot.y);
        Vector2 uv0 = new Vector2((uv.x + size.x * 0.5f) / size.x, (uv.y + size.y * 0.5f) / size.y);

        //支持贴图缩放
        uv0 *= texScale;
        uv0 -= Vector2.one * (texScale - 1f) * 0.5f;

        vertex.uv0 = uv0;
        vh.AddVert(vertex);
    }

    private void AddTriangle(VertexHelper vh, int count)
    {
        for (int i = 1; i <= count; i++)
        {
            if (i < count)
            {
                vh.AddTriangle(0, i, i + 1);
            }
            else
            {
                if (type == Type.Filled)
                {
                    if (fillAmount == 1)
                    {
                        vh.AddTriangle(0, i, 1);
                    }
                }
                else
                {
                    vh.AddTriangle(0, i, 1);
                }
            }
        }
    }

#if UNITY_EDITOR
    [UnityEditor.MenuItem("BToolkit/创建 ImagePro")]
    [UnityEditor.MenuItem("GameObject/BToolkit/创建 ImagePro")]
    static void Create()
    {
        GameObject go = new GameObject("ImagePro");
        go.layer = LayerMask.NameToLayer("UI");
        go.transform.SetParent(UnityEditor.Selection.activeGameObject.transform, false);
        go.AddComponent<ImagePro>();
        UnityEditor.EditorUtility.SetDirty(go);
    }
#endif

}